using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

using Irrlicht;
using Irrlicht.Video;
using Irrlicht.Scene;
using Irrlicht.Core;
using System.Threading;
using Mud.Client.WinApplication.Graphics;
using Mud.Common;
using Mud.Common.Physical;

namespace Mud.Client.WinApplication
{
    public class AreaView
    {
        private ICameraSceneNode _camera;
        private IrrlichtEventReceiver _receiver;


        #region Properties

        /// <summary>
        /// Current device.
        /// </summary>
        public IrrlichtDevice Device
        {
            get { return _device; }
        }
        private IrrlichtDevice _device;

        /// <summary>
        /// Client.
        /// </summary>
        public Client Client
        {
            get { return _client; }
        }
        private Client _client;

        /// <summary>
        /// Creatures.
        /// </summary>
        public IDictionary<Creature, CreatureModel> CreatureModels
        {
            get { return _creatures; }
        }
        private Dictionary<Creature, CreatureModel> _creatures = new Dictionary<Creature, CreatureModel>();


        /// <summary>
        /// Land square models.
        /// </summary>
        public LandSquareModel[,] LandModels
        {
            get { return _areaSquares; }
        }
        private LandSquareModel[,] _areaSquares;


        /// <summary>
        /// Land dimensions.
        /// </summary>
        public Size LandSize
        {
            get { return _areaSize; }
        }
        private Size _areaSize;

        public CreatureModel PlayerModel
        {
            get { return _playerModel; }
        }
        private CreatureModel _playerModel;



        #endregion

        public AreaView(Client client)
        {
            if (client == null)
                throw new ArgumentNullException("client");
            _client = client;
            _client.ClientNewPlayerCharacter += ClientNewCharacter;
            _client.ClientAreaChanged += ClientAreaChanged;
            _client.ClientStartedPlaying += ClientStartedPlaying;
            _client.ClientEndedPlaying += ClientEndedPlaying;
            _device = new IrrlichtDevice(DriverType.DIRECT3D8, new Dimension2D(600, 400), 32, false, false, true);
            _device.ResizeAble = false;
            _receiver = new IrrlichtEventReceiver(this);
            _receiver.PlayerModelClicked += PlayerModelClicked;
            _receiver.CreatureModelClicked += CreatureModelClicked;
            _receiver.LandModelClicked += LandModelClicked;
            _device.EventReceiver = _receiver;
            RefreshCaption();
        }


        public void Update()
        {
            _device.VideoDriver.BeginScene(true, true, new Color(0, 100, 100, 100));
            _device.SceneManager.DrawAll();
            _device.VideoDriver.EndScene();
        }

        #region Client event handling

        /// <summary>
        /// Called when the client starts to control a character.
        /// </summary>
        private void ClientNewCharacter(Creature oldCharacter, Creature newCharacter)
        {
            if (oldCharacter != null)
                oldCharacter.GameObjectChanged -= ClientPlayerCharacterChanged;
            if (newCharacter != null)
                newCharacter.GameObjectChanged += ClientPlayerCharacterChanged;

            if (_playerModel != null)
                _playerModel.RemoveFromScene();

            _playerModel = new CreatureModel(_device, newCharacter);
            _playerModel.AddToScene();
            RefreshCaption();
            RepositionCamera();
        }

        /// <summary>
        /// Called when the area is replaced with new area.
        /// </summary>
        private void ClientAreaChanged(Area oldArea, Area newArea)
        {
            if (oldArea != null)
            {
                oldArea.AreaChanged -= AreaChanged;
                oldArea.CreatureAdded -= AreaCreatureAdded;
                oldArea.CreatureRemoved -= AreaCreatureRemoved;
                oldArea.ObjectAdded -= AreaObjectAdded;
                oldArea.ObjectRemoved -= AreaObjectRemoved;

            }
            if (newArea != null)
            {
                newArea.AreaChanged += AreaChanged;
                newArea.CreatureAdded += AreaCreatureAdded;
                newArea.CreatureRemoved += AreaCreatureRemoved;
                newArea.ObjectAdded += AreaObjectAdded;
                newArea.ObjectRemoved += AreaObjectRemoved;
            }
            RefreshCaption();
            CreateCurrentClientArea();
        }

        /// <summary>
        /// Called when the clients player character characteristics change.
        /// </summary>
        private void ClientPlayerCharacterChanged(GameObject o)
        {
            RefreshCaption();
            RepositionCamera();
        }

        /// <summary>
        /// Called when the client starts to play.
        /// </summary>
        private void ClientStartedPlaying()
        {
        }

        /// <summary>
        /// Called when the client ends play.
        /// </summary>
        private void ClientEndedPlaying()
        {
        }

        /// <summary>
        /// Called when the current area changes.
        /// </summary>
        private void AreaChanged(Area a, Location position)
        {
            if (_areaSquares[position.X, position.Y] != null)
                _areaSquares[position.X, position.Y].RemoveFromScene();
            LandSquareModel square = new LandSquareModel(_device, a.Land[position.X, position.Y]);
            square.AddToScene();
            _areaSquares[position.X, position.Y] = square;
        }

        /// <summary>
        /// Called when a creature is added to the area.
        /// </summary>
        private void AreaCreatureAdded(Area a, Creature c)
        {
            if (c == _client.PlayerCharacter)
                return;
            CreatureModel model = new CreatureModel(_device, c);
            _creatures.Add(c, model);
            model.AddToScene();
        }

        /// <summary>
        /// Called when a creature is removed from the area.
        /// </summary>
        private void AreaCreatureRemoved(Area a, Creature c)
        {
            if (c == _client.PlayerCharacter)
                return;
            CreatureModel model = _creatures[c];
            model.RemoveFromScene();
            _creatures.Remove(c);
        }


        /// <summary>
        /// Called when a object is added to the area.
        /// </summary>
        private void AreaObjectAdded(Area a, Object o)
        {
        }

        /// <summary>
        /// Called when a object is removed from the area.
        /// </summary>
        private void AreaObjectRemoved(Area a, Object o)
        {
        }

        /// <summary>
        /// Called when an object changes (but not the player character).
        /// </summary>
        private void AreaObjectChanged(GameObject o)
        {
        }

        #endregion


        #region GUI event handling

        private void LandModelClicked(Model model, bool leftButton)
        {
            LandSquareModel l = model as LandSquareModel;
            Logger.Log(LogType.Debug, "Irrlicht", "Land {0},{1} clicked.", l.LandSquare.X, l.LandSquare.Y);
			if(leftButton)
				_client.RequestMoveCharacter(new Location(l.LandSquare.X, l.LandSquare.Y), Mud.Common.Physical.MovementType.Running);
			else
                _client.RequestMoveCharacter(new Location(l.LandSquare.X, l.LandSquare.Y), Mud.Common.Physical.MovementType.Walking);
		}

        private void PlayerModelClicked(Model model, bool leftButton)
        {
            CreatureModel p = model as CreatureModel;
            Logger.Log(LogType.Debug, "Irrlicht", "Player clicked.");
        }

        private void CreatureModelClicked(Model model, bool leftButton)
        {
            CreatureModel c = model as CreatureModel;
            Logger.Log(LogType.Debug, "Irrlicht", "Creature {0} clicked.", c.Creature);
        }

        #endregion

        #region Helper methods

        /// <summary>
        /// Refresh the window caption.
        /// </summary>
        private void RefreshCaption()
        {
            StringBuilder builder = new StringBuilder();
            if (_client.Playing)
            {
                builder.Append("Character '");
                builder.Append(_client.PlayerCharacter.Name);
                builder.Append("' / area '");
                builder.Append(_client.PlayerArea.Name);
                builder.Append("'");
            }
            _device.WindowCaption = builder.ToString();
        }

        private void CreateCurrentClientArea()
        {
            Creature pc = _client.PlayerCharacter;
            Area area = _client.PlayerArea;

            _device.SceneManager.Clear();


            _playerModel = new CreatureModel(_device, pc);
            _playerModel.AddToScene();

            _camera = _device.SceneManager.AddCameraSceneNode(null, new Vector3D(pc.Location.X, pc.Location.Y + 2, 16), new Vector3D(pc.Location.X, pc.Location.Y, 0), -1);
            _camera.FOV = 1.5f;
            _camera.UpVector = new Vector3D(0, -1, 0);

            ILightSceneNode light = _device.SceneManager.AddLightSceneNode(_playerModel.Node, new Vector3D(0, 8, 0), new Colorf(1, 1, 1), 10, -1);

            _areaSize = area.Size;
            _areaSquares = new LandSquareModel[area.Size.Width, area.Size.Height];
        }


        private void RepositionCamera()
        {
            if (_camera != null)
            {
                Creature pc = _client.PlayerCharacter;
                _camera.Position = new Vector3D(pc.Location.X, pc.Location.Y + 2, 16);
                _camera.Target = new Vector3D(pc.Location.X, pc.Location.Y, 0);
            }
        }

        #endregion

    }
}
